package com.xiekong.rpc.server.spring;

import com.xiekong.rpc.annotation.RpcService;
import com.xiekong.rpc.bean.RpcRequest;
import com.xiekong.rpc.bean.RpcResponse;
import com.xiekong.rpc.codec.RpcDecoder;
import com.xiekong.rpc.codec.RpcEncoder;
import com.xiekong.rpc.handler.RpcServerHandler;
import com.xiekong.rpc.registry.ServiceRegistry;
import com.xiekong.rpc.server.RpcServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Map;

/**
 * Netty RPC服务器
 *
 * @author XieKong
 */
public class SpringRpcServer extends RpcServer implements ApplicationContextAware, InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(com.xiekong.rpc.server.RpcServer.class);
    /**
     * 存放服务名与服务对象之间的映射关系
     */
    private Map<String, Object> serviceMap;

    public SpringRpcServer(String serviceAddress, ServiceRegistry serviceRegistry) {
        super(serviceAddress, serviceRegistry);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.serviceMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        // 获取所有带有RpcService注解的Spring Bean
//        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(RpcService.class);
//        if (MapUtils.isNotEmpty(serviceBeanMap)) {
//            for (Object serviceBean : serviceBeanMap.values()) {
//                String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).value().getName();
//                this.serviceMap.put(interfaceName, serviceBean);
//            }
//        }
    }

    /**
     * InitializingBean接口为bean提供了初始化方法的方式，
     * 它只包括afterPropertiesSet方法，凡是继承该接口的类，
     * 在初始化bean的时候会执行该方法。
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        startup();
    }

    @Override
    public void startup() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup);
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024)
//                    .childOption(ChannelOption.SO_KEEPALIVE, false)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            // 解码RPC请求
                            ch.pipeline().addLast(new RpcDecoder(RpcRequest.class));
                            // 处理RPC请求
                            ch.pipeline().addLast(new RpcServerHandler(serviceMap));
                            // 编码RPC响应
                            ch.pipeline().addLast(new RpcEncoder(RpcResponse.class));
                        }
                    });

            // 获取RPC服务器的IP地址与端口号
            String[] addressArray = StringUtils.split(getServiceAddress(), ":");
            String host = addressArray[0];
            int port = Integer.parseInt(addressArray[1]);
            // 启动RPC服务器
            ChannelFuture future = bootstrap.bind(host, port).sync();

            // 注册RPC服务地址
            for (String interfaceName : this.serviceMap.keySet()) {
                getServiceRegistry().register(interfaceName, getServiceAddress());
            }
            //等待客户端通道关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("", e);
        }
        finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
